package main

import (
	"context"
	"grpc_etcd/proto"
	"fmt"
	"go.etcd.io/etcd/clientv3"
	"google.golang.org/grpc"
	"math/rand"
	"time"
)
/**
grpc客户端
随机选择法：从etcd注册表随机选择一个rpc服务ip,port进行链接，实现LB负载均衡
注：rand.Intn(3)函数每次开始生成的随机数都是一致的，故使用rand.Seed(time.Now().Unix())随机因子来解决此问题[设置随机数种子，可以保证每次随机都是随机的]
 */
func main(){
	conf := &RpcClientConf{
		endpoints:[]string{"http://localhost:2379"},
		key:"student_server",
	}
	cli3,err := clientv3.New(clientv3.Config{Endpoints:conf.endpoints})
	if err!=nil{
		panic(err)
	}
	//ticker + for 循环实现多客户端并发请求rpc
	t := time.NewTicker( 2 * time.Second)
	for{
		rpcClient,err := NewRpcClient(conf, func(conn *grpc.ClientConn) {
			//创建远程链接客户端
			studentServerClient := proto.NewStudentServerClient(conn)
			student,err := studentServerClient.GetStudent(context.TODO(),&proto.ReqStudent{Id:3})
			if err!=nil{
				fmt.Println(err.Error())
				return
			}
			fmt.Println(student)
		})
		if err !=nil {
			panic(err)
		}
		rpcClient.Do(cli3)

		<-t.C
	}
}

type ExecuteRpcFunc func(conn *grpc.ClientConn)

type RpcClient struct{
	key string
	executerpcFunc ExecuteRpcFunc
}
type RpcClientConf struct{
	endpoints []string
	key string
}
func NewRpcClient(conf *RpcClientConf,fc ExecuteRpcFunc)(*RpcClient,error){
	return &RpcClient{
		executerpcFunc:fc,
		key:conf.key,
	},nil
}
func (rpcClient *RpcClient)Do(cli3 *clientv3.Client){
	//服务发现，获取etcd里的服务地址信息
	getResp,err := cli3.Get(context.TODO(),rpcClient.key,clientv3.WithPrefix())//通过前缀获取所有相匹配的服务
	if err!=nil{
		panic(err)
	}
	serverMap := map[int]string{}
	for i,kv := range getResp.Kvs {
		serverMap[i] = string(kv.Value)
	}

	/**
	生成随机数实现负载均衡
	 */
	rand.Seed(time.Now().Unix())
	index := rand.Intn(len(serverMap))//[0,n)

	//dial链接到随机选择的rpc服务
	ctx,_:= context.WithTimeout(context.TODO(),5 * time.Second)
	conn,err := grpc.DialContext(ctx,serverMap[index],grpc.WithInsecure())
	if err!=nil{
		//链接建立失败
		panic(err)
	}
	fmt.Println("请求rpc服务：",serverMap[index])
	//执行远程rpc方法
	rpcClient.executerpcFunc(conn)
}

//默认rpc调用方式写法
func defversion1(){
	conn,err := grpc.Dial("127.0.0.1:8080",grpc.WithInsecure())
	if err!=nil{
		panic(err)
	}
	//创建远程链接客户端
	studentServerClient := proto.NewStudentServerClient(conn)
	student,err := studentServerClient.GetStudent(context.TODO(),&proto.ReqStudent{Id:3})
	if err!=nil{
		panic(err)
	}
	fmt.Println(student)
}